﻿using DotNetCommon;
using DotNetCommon.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;

namespace DBUtil.Builders;

public class BaseBuilder
{
    internal DBAccess db { get; private set; }

    internal BaseBuilder(DBAccess db)
    {
        AssertUtil.NotNull(db);
        this.db = db;
    }

    #region 超时设置
    public int? TimeoutSeconds { get; protected set; }
    public virtual BaseBuilder CommandTimeout(int timeoutSeconds)
    {
        TimeoutSeconds = timeoutSeconds;
        return this;
    }
    public virtual BaseBuilder CommandTimeoutIf(bool condition, int timeoutSeconds)
        => condition ? CommandTimeout(timeoutSeconds) : this;
    #endregion
    #region RunWriteMonitor
    protected T RunWriteMonitor<T>(AfterWriteArgument arg, Func<T> func)
    {
        var res = func();
        if (db.Setting.HasAfterWriteMonitor) db.Setting.AfterWriteMonitorAction(arg).Wait();
        return res;
    }
    protected async Task<T> RunWriteMonitorAsync<T>(AfterWriteArgument arg, Func<Task<T>> func)
    {
        var res = await func();
        if (db.Setting.HasAfterWriteMonitor) await db.Setting.AfterWriteMonitorAction(arg);
        return res;
    }
    #endregion
}

public class WhereBuilder : BaseBuilder
{
    private struct WhereSegWrap(object value)
    {
        public object Value = value;
    }
    internal WhereBuilder(DBAccess db) : base(db) { }
    protected List<object> Filters { get; set; } = [];

    internal WhereBuilder SetFilters(List<object> filters)
    {
        this.Filters = filters;
        return this;
    }

    #region WhereSeg
    public virtual WhereBuilder WhereSeg<TAny>(Expression<Func<TAny, bool>> filter)
    {
        AssertUtil.NotNull(filter);
        Filters.Add(new WhereSegWrap(filter));
        return this;
    }
    public virtual WhereBuilder WhereSegIf<TAny>(bool condition, Expression<Func<TAny, bool>> filter)
    {
        if (!condition) return this;
        return WhereSeg(filter);
    }
    public virtual WhereBuilder WhereSeg<TAny, TAny2>(Expression<Func<TAny, TAny2, bool>> filter)
    {
        AssertUtil.NotNull(filter);
        Filters.Add(new WhereSegWrap(filter));
        return this;
    }
    public virtual WhereBuilder WhereSegIf<TAny, TAny2>(bool condition, Expression<Func<TAny, TAny2, bool>> filter)
    {
        if (!condition) return this;
        return WhereSeg(filter);
    }
    public virtual WhereBuilder WhereSeg<TAny, TAny2, TAny3>(Expression<Func<TAny, TAny2, TAny3, bool>> filter)
    {
        AssertUtil.NotNull(filter);
        Filters.Add(new WhereSegWrap(filter));
        return this;
    }
    public virtual WhereBuilder WhereSegIf<TAny, TAny2, TAny3>(bool condition, Expression<Func<TAny, TAny2, TAny3, bool>> filter)
    {
        if (!condition) return this;
        return WhereSeg(filter);
    }
    public virtual WhereBuilder WhereSeg<TAny, TAny2, TAny3, TAny4>(Expression<Func<TAny, TAny2, TAny3, TAny4, bool>> filter)
    {
        AssertUtil.NotNull(filter);
        Filters.Add(new WhereSegWrap(filter));
        return this;
    }
    public virtual WhereBuilder WhereSegIf<TAny, TAny2, TAny3, TAny4>(bool condition, Expression<Func<TAny, TAny2, TAny3, TAny4, bool>> filter)
    {
        if (!condition) return this;
        return WhereSeg(filter);
    }
    #endregion
    #region Where
    public virtual WhereBuilder Where(string filter)
    {
        AssertUtil.NotNullOrWhiteSpace(filter);
        Filters.Add(filter);
        return this;
    }
    public virtual WhereBuilder WhereIf(bool condition, string filter)
    {
        if (!condition) return this;
        return Where(filter);
    }
    #endregion  

    /// <summary>
    /// 处理 Filter,兼容
    /// <list type="bullet">
    /// <item>id=1</item>
    /// <item>(id,name)=>id>10</item>
    /// <item>i=>i.id==1</item>
    /// </list>
    /// </summary>
    protected string DealFilter(List<object> Filters, Dictionary<ParameterExpression, object> midValues = null, IEnumerable<KeyValuePair<ParameterExpression, string>> aliases = null)
    {
        AssertUtil.NotNullOrEmpty(Filters);
        //处理过滤条件
        var filterSqls = new List<string>();
        if (Filters.IsNotNullOrEmpty())
        {
            for (int i = 0; i < Filters.Count; i++)
            {
                var filter = Filters[i];
                var isSeg = false;
                if (filter is WhereSegWrap wrap)
                {
                    isSeg = true;
                    filter = wrap.Value;
                }
                if (filter is string str)
                {
                    filterSqls.Add(str);
                    continue;
                }
                if (filter is not LambdaExpression lambda) throw new Exception($"必须为LambdaExpression!");
                var reduce = ExpressionHelper.ReduceLambda(lambda, initParameters: aliases?.Select(i => i.Key).ToList(), isKeepCallBack: BuilderHelper.IsKeepCallBackForSubSelect);
                if (midValues.IsNotNullOrEmpty()) reduce.midValues.Add(midValues);
                midValues = reduce.midValues;

                var ret = BuilderHelper.GetConstant(reduce.exp, midValues);
                if (ret.Success && ret.Data is bool flag)
                {
                    filterSqls.Add(flag ? "1=1" : "1=0");
                    continue;
                }
                lambda = reduce.exp as LambdaExpression;
                string tmp = string.Empty;
                List<KeyValuePair<ParameterExpression, string>> aliasesMap = null;
                if (isSeg) aliasesMap = GetWhereSegAliasRule(lambda);
                else if (this is not SelectBuilderBase)
                {
                    //非查询,如: update/delete 需要禁用别名
                    aliasesMap = lambda.Parameters.Select(i => new KeyValuePair<ParameterExpression, string>(i, null)).ToList();
                }
                aliasesMap ??= [];
                if (aliases.IsNotNullOrEmpty()) aliasesMap.AddRange(aliases);
                tmp = BuilderHelper.ParseSql(lambda, db, aliasesMap: aliasesMap, midValues: midValues);
                if (tmp.IsNotNullOrWhiteSpace()) filterSqls.Add(tmp);
            }
        }
        if (filterSqls.Count == 0) return string.Empty;
        else if (filterSqls.Count == 1) return filterSqls.FirstOrDefault();
        else return filterSqls.Select(i => $"({i})").ToStringSeparated(" and ");
    }

    /// <summary>
    /// 计算 WhereSeg 中参数的别名
    /// </summary>
    protected virtual List<KeyValuePair<ParameterExpression, string>> GetWhereSegAliasRule(LambdaExpression lambda)
    {
        if (lambda.Parameters.Count == 0) return null;
        #region 思路
        // 在 builder 下没有输入 alias 的机会
        // 简单类型: WhereSeg<int>(age=>age>10) 注意: 不能是 _=>_>10 抛异常
        //     update/insert:
        //         =>sql: age>10
        //     select: 
        //         只有一个Entity的时候
        //             =>sql: t.age>10
        //         否则使用最后一个的:
        //             =>sql: t3.age>10
        //
        // class:
        //     WhereSeg<Person>(i=>i.age>10)            
        //     update/delete:
        //         => age>10
        //     select:
        //         如果是 "_" 则直接取最后一个entity的别名
        //         有且只有一个匹配到的 Entity 的时, 使用匹配到的entity:
        //             sql: t2.age>10
        //         否则:
        //             sql: i.age>10
        #endregion
        var map = new List<KeyValuePair<ParameterExpression, string>>(lambda.Parameters.Count);
        var isSelect = this is SelectBuilderBase;

        for (var i = 0; i < lambda.Parameters.Count; i++)
        {
            var param = lambda.Parameters[i];
            if (DBAccess.IsSimple(param.Type))
            {
                // WhereSeg<int>(age=>age>10)
                if (param.Name == "_") throw new Exception($"'_' 错误, 禁止写法: [select|insert|update]Builder.WhereSeg<int>(_ => _ > 0)");
                if (isSelect)
                {
                    var selectBase = this as SelectBuilderBase;
                    var alias = selectBase.EntityAliases.Last().Alias;
                    map.Add(new KeyValuePair<ParameterExpression, string>(param, alias));
                }
                else
                {
                    map.Add(new KeyValuePair<ParameterExpression, string>(param, db.AddQuote(param.Name)));
                }
            }
            else
            {
                //WhereSeg<Person>(i=>i.age>10)
                if (isSelect)
                {
                    var selectBase = this as SelectBuilderBase;
                    var alias = selectBase.EntityAliases.Last().Alias;
                    if (param.Name == "_")
                    {
                        map.Add(new KeyValuePair<ParameterExpression, string>(param, alias));
                    }
                    else
                    {
                        var arr = selectBase.EntityAliases.Select(i => i.EntityInfo.Type).ToList();
                        var idx = arr.FindIndex(i => i == param.Type);
                        var idx2 = arr.FindLastIndex(i => i == param.Type);
                        if (idx >= 0 && idx == idx2)
                        {
                            //只有一个类型,如: SelectBuilder<Person,Student,Dog>().WhereSeg<Person>(i=>)
                            map.Add(new KeyValuePair<ParameterExpression, string>(param, selectBase.EntityAliases[idx].Alias));
                        }
                        else
                        {
                            //有多个相同的类型,如: SelectBuilder<Person,Student,Person>().WhereSeg<Person>(i=>)
                            //这种时候就不再从 SelectBuilder<>.Alias() 中找, 直接使用 WhereSeg<Person>(i=>) 中的
                            map.Add(new KeyValuePair<ParameterExpression, string>(param, param.Name));
                        }
                    }
                }
                else
                {
                    map.Add(new KeyValuePair<ParameterExpression, string>(param, null));
                }
            }
        }
        return map;
    }
}
